#include "stdio.h"
#include "stdlib.h"
#include "mpi.h"

#define a(x,y) a[x*M+y]
/*A为M*M矩阵*/
#define A(x,y) A[x*M+y]
#define l(x,y) l[x*M+y]
#define u(x,y) u[x*M+y]
#define floatsize sizeof(float)
#define intsize sizeof(int)

int M,N;
int m;
float *A;
int my_rank;
int p;
MPI_Status status;

void fatal(char *message)
{
    printf("%s\n",message);
    exit(1);
}

void Environment_Finalize(float *a,float *f)
{
    free(a);
    free(f);
}

int main(int argc, char **argv)
{
    int i,j,k,my_rank,group_size;
    int i1,i2;
    int v,w,MM;
    float *a,*f,*l,*u;
    FILE *fdA;
    double time1,time2;

    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD,&group_size);
    MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);

    p=group_size;
    time1=MPI_Wtime();
    if (my_rank==0)
    {
        fdA=fopen("dataIn.txt","r");
        fscanf(fdA,"%d",&M);
        A=(float *)malloc(floatsize*M*M);
        for(i = 0; i < M; i ++)
            for(j = 0; j < M; j ++)
                fscanf(fdA, "%f", A+i*M+j);
        fclose(fdA);
    }

    /*0号进程将M广播给所有进程*/
    MPI_Bcast(&M,1,MPI_INT,0,MPI_COMM_WORLD);
    m=M/p;
    if (M%p!=0) m++;

    /*分配至各进程的子矩阵大小为m*M*/
    a=(float*)malloc(floatsize*m*M);

    /*各进程为主行元素建立发送和接收缓冲区*/
    f=(float*)malloc(floatsize*M);

    /*0号进程为l和u矩阵分配内存，以分离出经过变换后的A矩阵中的l和u矩阵*/
    if (my_rank==0)
    {
        l=(float*)malloc(floatsize*M*M);
        u=(float*)malloc(floatsize*M*M);
    }

    
    if (a==NULL) fatal("allocate error\n");

	
    if (my_rank==0)
    {
        for(i=0;i<m;i++)
            for(j=0;j<M;j++)
                a(i,j)=A(i,j);
        for(i=m;i<M;i++)
            
        {
            i1 = i/m;
            i2 = i%m;
            MPI_Send(&A(i,0),M,MPI_FLOAT,i1,i2,MPI_COMM_WORLD);
        }
    }
    else if((my_rank*m<M))
    {
        for(i=0;i<m;i++){
	    if(my_rank*m+i>=M) break;
            MPI_Recv(&a(i,0),M,MPI_FLOAT,0,i,MPI_COMM_WORLD,&status);
    	}
    }

    for(i=0;i<p;i++){
	if(i*m>=M) break;
        for(j=0;j<m;j++)
    {
        /*i号进程负责广播主行元素*/
      if(i*m+j>=M) break;  
      if (my_rank==i)
        {
	    
            v=i*m+j;
	        for (k=v;k<M;k++)
                f[k]=a(j,k);

            MPI_Bcast(f,M,MPI_FLOAT,my_rank,MPI_COMM_WORLD);
        }
        else
        {
            v=i*m+j;
            MPI_Bcast(f,M,MPI_FLOAT,i,MPI_COMM_WORLD);
        }

        if (my_rank==i)
            for(k=j+1;k<m;k++)
        {
            a(k,v)=a(k,v)/f[v];
            for(w=v+1;w<M;w++)
                a(k,w)=a(k,w)-f[w]*a(k,v);
        }

        
        if (my_rank>i)
            for(k=0;k<m;k++)
        {
            a(k,v)=a(k,v)/f[v];
            for(w=v+1;w<M;w++)
                a(k,w)=a(k,w)-f[w]*a(k,v);
        }
    }
    }

    /*0号进程从其余各进程中接收子矩阵a，得到经过变换的矩阵A*/
    if (my_rank==0)
    {
        for(i=0;i<m;i++)
            for(j=0;j<M;j++)
                A(i,j)=a(i,j);
    }
    if ((my_rank!=0)&&(my_rank*m<M))
    {
        for(i=0;i<m;i++){
	    if(my_rank*m+i>=M) break;
            MPI_Send(&a(i,0),M,MPI_FLOAT,0,m*my_rank+i,MPI_COMM_WORLD);
        }
    }
    if(my_rank==0)
    {
        for(i=1;i<p;i++)
            for(j=0;j<m;j++)
        {
	    if(i*m+j>=M) break;
            MPI_Recv(&a(j,0),M,MPI_FLOAT,i,m*i+j,MPI_COMM_WORLD,&status);
            for(k=0;k<M;k++)
                A((i*m+j),k)=a(j,k);
        }
    }

    if (my_rank==0)
    {
        for(i=0;i<M;i++)
            for(j=0;j<M;j++)
                u(i,j)=0.0;
        for(i=0;i<M;i++)
            for(j=0;j<M;j++)
                if (i==j)
                    l(i,j)=1.0;
        else
            l(i,j)=0.0;
        for(i=0;i<M;i++)
            for(j=0;j<M;j++)
                if (i>j)
                    l(i,j)=A(i,j);
        else
            u(i,j)=A(i,j);
        printf("Input of file \"dataIn.txt\"\n");
        printf("%d\t\n",M);
        for(i=0;i<M;i++)
        {
            for(j=0;j<M;j++)
                printf("%f ",A(i,j));
            printf("\n");
        }
        printf("\nOutput of LU operation\n");
        printf("Matrix L:\n");
        for(i=0;i<M;i++)
        {
            for(j=0;j<M;j++)
                printf("%f ",l(i,j));
            printf("\n");
        }
        printf("Matrix U:\n");
        for(i=0;i<M;i++)
        {
            for(j=0;j<M;j++)
                printf("%f ",u(i,j));
            printf("\n");
        }
    }
    time2=MPI_Wtime();
    printf("The whole time is %f\n",time2-time1);
    MPI_Finalize();
    Environment_Finalize(a,f);
    return(0);
}